﻿
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;

using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;

namespace iTool.SQL.AIHandle.Common
{
    public class SqlBuiltinScalarFunctionCallAnalysis : SqlAnalysisBase
    {
        public SqlBuiltinScalarFunctionCallAnalysis(SqlAnalysisContext context) : base(context)
        {
        }
        // 自定义函数？
        public override void Analysis()
        {

            var builtin = (SqlBuiltinScalarFunctionCallExpression)base.CodeObject;

            string schemaName = string.Empty;

            switch (builtin.FunctionName.ToLower())
            {
                // 走索引 (这里不应该 拼接上去)
                case "search":
                    {
                        SqlLikeBooleanExpression likeExpression = base.CodeObject.Parent is SqlLikeBooleanExpression ? (SqlLikeBooleanExpression)base.CodeObject.Parent : null;
                        SqlInBooleanExpression inExpression = base.CodeObject.Parent is SqlInBooleanExpression ? (SqlInBooleanExpression)base.CodeObject.Parent : null;

                        if (likeExpression == null && inExpression == null)
                        {
                            throw new Exception("please  use in or like expression");
                        }

                        // where search(..., 'preToken', 20) --(..., token, size)
                        // where search(..., 'pretoken') --(..., token)
                        // where search(..., 1, 20) --(..., page, size)
                        // where search(..., 20) --(..., size)
                        // search(must(name),shoube(tag),notmust(productName),distmap(x,y), name) in ('%jian%','包吃','橘子',100,'xxx')
                        SqlInBooleanExpressionCollectionValue? inValues = null;
                        IndexTypeOptions indexType = IndexTypeOptions.None;
                        string token = string.Empty;
                        int page = 0, size = 0;
                        List<ChildIndexFn> childIndexFns = new List<ChildIndexFn>();
                        

                        if (likeExpression != null)
                        {
                            childIndexFns.Add(new ChildIndexFn
                            {
                                Fields = new List<string>(),
                                IndexMode = IndexModeOptions.Shoube,
                                Values = new List<string>()
                            });
                            childIndexFns[0].Values.Add(likeExpression.LikePattern.ToString());
                        }
                        else
                        {
                            inValues = inExpression?.Children.OfType<SqlInBooleanExpressionCollectionValue>().First();
                        }

                        base.Next(beforeAction: (index, code, analysis) =>
                        {
                            // field
                            if (code is SqlScalarRefExpression sqlScalarRef)
                            {
                                var expression = sqlScalarRef.Children.OfType<SqlObjectIdentifier>().First();
                                //schemaName = expression.SchemaName.Value;
                                childIndexFns[0].Fields.Add(string.Intern(expression.ObjectName.Value.ToUpper()));
                                return true;
                            }
                            // field
                            else if (code is SqlColumnRefExpression sqlColumnRef)
                            {
                                childIndexFns[0].Fields.Add(string.Intern(sqlColumnRef.ColumnName.ToString().ToUpperInvariant()));
                                return true;
                            }
                            // fn
                            else if (code is SqlBuiltinScalarFunctionCallExpression builtin)
                            {
                                // 子函数 说明多条件
                                switch (builtin.FunctionName.ToLower())
                                {
                                    case "shoube":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.Shoube,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    case "must":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.Must,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    case "mustnot":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.MustNot,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    case "distmap":
                                    case "mustdistmap":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.MustDistMap,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    case "mustnotdistmap":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.MustNotDistMap,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    case "shoubedistmap":
                                        childIndexFns.Add(new ChildIndexFn
                                        {
                                            IndexMode = IndexModeOptions.ShoubeDistMap,
                                            Fields = new List<string>(),
                                            Values = new List<string>()
                                        });
                                        break;
                                    default:
                                        throw new Exception("not suport " + builtin.FunctionName);
                                }

                                foreach (var item in builtin.Children)
                                {
                                    // field
                                    if (item is SqlScalarRefExpression sqlScalarRef1)
                                    {
                                        var expression = sqlScalarRef1.Children.OfType<SqlObjectIdentifier>().First();
                                        childIndexFns[^1].Fields.Add(string.Intern(expression.ObjectName.Value.ToUpper()));
                                        base.Context.Fields.Add(new KeyValuePair<string, string>(String.Empty, expression.ObjectName.Value.ToUpper()));
                                    }
                                    // field
                                    else if (item is SqlColumnRefExpression sqlColumnRef1)
                                    {
                                        childIndexFns[^1].Fields.Add(string.Intern(sqlColumnRef1.ColumnName.ToString().ToUpperInvariant()));
                                        var expression = sqlColumnRef1.Children.OfType<SqlObjectIdentifier>().First();
                                        string fieldName = expression.ObjectName.Value.ToUpper();
                                        string schemaName = expression.SchemaName.Value.ToUpper();
                                        base.Context.Fields.Add(new KeyValuePair<string, string>(schemaName, fieldName));
                                    }
                                    else if (item is SqlLiteralExpression intValue)
                                    {
                                        childIndexFns[^1].Values.Add(intValue.Value);
                                    }
                                }
                            }
                            // value
                            else if (code.GetType().FullName.Contains("IntegerLiteralExpression") && code is SqlLiteralExpression intValue)
                            {
                                // 值 数据
                                switch (base.CodeObject.Children.Count() - index)
                                {
                                    case 1:
                                        size = int.Parse(intValue.Value);
                                        // where search(..., size) 
                                        if (indexType == IndexTypeOptions.None)
                                            indexType = IndexTypeOptions.OnlySize;
                                        break;
                                    case 2:
                                        // where search(..., page, size) 
                                        page = int.Parse(intValue.Value);
                                        indexType = IndexTypeOptions.PageAndSize;
                                        break;
                                }
                                //System.Console.WriteLine("index:"+ index + "_" + base.CodeObject.Children.Count());
                                return true;
                            }
                            else if (code.GetType().FullName.Contains("StringLiteralExpression") && code is SqlLiteralExpression stringValue)
                            {
                                token = stringValue.Value;
                                // 值 数据
                                switch (base.CodeObject.Children.Count() - index)
                                {
                                    case 1:
                                        // where search(..., 'pretoken')
                                        if (indexType == IndexTypeOptions.None)
                                            indexType = IndexTypeOptions.OnlyToken;
                                        break;
                                    case 2:
                                        // where search(..., 'preToken', size)
                                        indexType = IndexTypeOptions.TokenAndSize;
                                        break;
                                }
                                //System.Console.WriteLine("index:"+ index + "_" + base.CodeObject.Children.Count());
                                return true;
                            }
                            return false;
                        });

                        if (inValues != null)
                        {
                            var valueList = inValues.Children.ToList();
                            for (int i = 0; i < valueList.Count(); i++)
                            {
                                if (valueList[i] is SqlLiteralExpression valueObj)
                                {
                                    childIndexFns[i].Values.Add(valueObj.Value);
                                }
                                else
                                {
                                    throw new Exception("in error value index:" + i + ". value must is const");
                                }
                            }
                        }

                        base.Context.Indexs.Add(new SearchIndexOptions(
                            base.CodeObject.Parent.Sql,
                            likeExpression == null ? inExpression.HasNot : likeExpression.HasNot,
                            indexType,
                            childIndexFns,
                            base.Context.Tables[base.Context.SubIndex].Value,
                            token, page, size));
                    }
                    break;
                case "distance":
                    this.Context.CustomFunctions ??= new List<string>();
                    this.Context.CustomFunctions.Add("distance");
                    break;

                case "sortdist":
                    {
                        if (!(base.CodeObject.Parent is SqlOrderByItem))
                        {
                            throw new Exception("sortdist only use order by");
                        }

                        List<string> Fields = new List<string>();
                        base.Next(
                            beforeAction: (index, code, analysis) =>
                            {
                                if (code is SqlScalarRefExpression sqlScalarRef)
                                {
                                    var expression = sqlScalarRef.Children.OfType<SqlObjectIdentifier>().First();
                                    //schemaName = expression.SchemaName.Value;
                                    Fields.Add(string.Intern(expression.ObjectName.Value.ToUpper()));
                                    return true;
                                }
                                else if (code is SqlColumnRefExpression sqlColumnRef)
                                {
                                    Fields.Add(string.Intern(sqlColumnRef.ColumnName.ToString().ToUpperInvariant()));
                                    return true;
                                }
                                else if (code is SqlLiteralExpression intValue)
                                {
                                    base.Context.SortByOptions[^1].Values ??= new List<string>();
                                    base.Context.SortByOptions[^1].Values.Add(intValue.Value);
                                }
                                return false;
                            });
                        base.Context.SortByOptions[^1].Fields = Fields;
                    }
                    break;
                default:
                    System.Console.WriteLine(builtin.FunctionName);
                    break;
                    //throw new System.Exception("not support:" + base.CodeObject.Parent.Sql);
            }
        }

        public override bool Validation(SqlCodeObject codeObject) => true;
    }
}
